#region Header

// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <author name="Daniel Grunwald"/>
//     <version>$Revision: 3090 $</version>
// </file>

#endregion Header

namespace ICSharpCode.TextEditor.Util
{
    using System;
    using System.Collections.Generic;
    using System.Threading;

    /// <summary>
    /// A IList{T} that checks that it is only accessed on the thread that created it, and that
    /// it is not modified while an enumerator is running.
    /// </summary>
    sealed class CheckedList<T> : IList<T>
    {
        #region Fields

        readonly IList<T> baseList;
        readonly int threadID;

        int enumeratorCount;

        #endregion Fields

        #region Constructors

        public CheckedList()
            : this(new List<T>())
        {
        }

        public CheckedList(IList<T> baseList)
        {
            if (baseList == null)
                throw new ArgumentNullException("baseList");
            this.baseList = baseList;
            this.threadID = Thread.CurrentThread.ManagedThreadId;
        }

        #endregion Constructors

        #region Properties

        public int Count
        {
            get {
                CheckRead();
                return baseList.Count;
            }
        }

        public bool IsReadOnly
        {
            get {
                CheckRead();
                return baseList.IsReadOnly;
            }
        }

        #endregion Properties

        #region Indexers

        public T this[int index]
        {
            get {
                CheckRead();
                return baseList[index];
            }
            set {
                CheckWrite();
                baseList[index] = value;
            }
        }

        #endregion Indexers

        #region Methods

        public void Add(T item)
        {
            CheckWrite();
            baseList.Add(item);
        }

        public void Clear()
        {
            CheckWrite();
            baseList.Clear();
        }

        public bool Contains(T item)
        {
            CheckRead();
            return baseList.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            CheckRead();
            baseList.CopyTo(array, arrayIndex);
        }

        public IEnumerator<T> GetEnumerator()
        {
            CheckRead();
            return Enumerate();
        }

        public int IndexOf(T item)
        {
            CheckRead();
            return baseList.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            CheckWrite();
            baseList.Insert(index, item);
        }

        public bool Remove(T item)
        {
            CheckWrite();
            return baseList.Remove(item);
        }

        public void RemoveAt(int index)
        {
            CheckWrite();
            baseList.RemoveAt(index);
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            CheckRead();
            return Enumerate();
        }

        void CheckRead()
        {
            if (Thread.CurrentThread.ManagedThreadId != threadID)
                throw new InvalidOperationException("CheckList cannot be accessed from this thread!");
        }

        void CheckWrite()
        {
            if (Thread.CurrentThread.ManagedThreadId != threadID)
                throw new InvalidOperationException("CheckList cannot be accessed from this thread!");
            if (enumeratorCount != 0)
                throw new InvalidOperationException("CheckList cannot be written to while enumerators are active!");
        }

        IEnumerator<T> Enumerate()
        {
            CheckRead();
            try {
                enumeratorCount++;
                foreach (T val in baseList) {
                    yield return val;
                    CheckRead();
                }
            } finally {
                enumeratorCount--;
                CheckRead();
            }
        }

        #endregion Methods
    }
}